
#include <memory.h>

#define LG2D_CORE

#include "lg2d_scene.h"
#include "lg2d_obj.h"

typedef struct {
    unsigned count;
    LGMaterial* material;
}DrawCmd;

static LGVertexAttr VERTEX_ATTR[] = {
    {LG_VERTEX_POSITION, LG_VERTEX_OFFSET(LG2DPoint, position)},
    {LG_VERTEX_COLOR, LG_VERTEX_OFFSET(LG2DPoint, color)},
    {LG_VERTEX_UV, LG_VERTEX_OFFSET(LG2DPoint, uv)},
};

static void
freeScene(LGRef* ref) {
	LG2DScene* scene = (LG2DScene*)ref;
	lgReleaseRef(scene->vertex);
	lgReleaseRef(scene->canvas);
    //lgReleaseRef(scene->shape);
	lgReleaseRef(scene->root);
    lgFreeList(scene->cmdList);
}

LG2D_API LG2DScene*
lg2dNewScene(unsigned width, unsigned height) {
	LG2DScene* scene = lgNewRef(sizeof(LG2DScene), freeScene);
	scene->width = width;
	scene->height = width;
	scene->vertex = lgNewVertex(VERTEX_ATTR,
        3, sizeof(LG2DPoint), LG2D_SCENE_VERTEX_CAP);
	scene->canvas = lgNewCanvas(width, height);
	scene->root = lg2dNewObj();
	lgRetainRef(scene->vertex);
	lgRetainRef(scene->canvas);
	lgRetainRef(scene->root);
    scene->cmdList = lgNewList(sizeof(DrawCmd), 32);

    LGMatrix view;
    lgIdentityMatrix(&view);
    LGVector scale = { 2.f / width, 2.f / height, 1.f, 1.f };
    lgScaleMatrix(&view, &scale);
    lgSetCanvasView(scene->canvas, &view);
	return scene;
}

LG2D_API void
lg2dDrawScene(LG2DScene* scene) {
    lgClearVertex(scene->vertex);
    lgClearCanvas(scene->canvas);
    lgClearList(scene->cmdList);

	LGMatrix transform;
	lgIdentityMatrix(&transform);
	lg2dVisitObj(scene->root, &transform, 0);
	lg2dDrawObj(scene, scene->root);
	lgUploadVertex(scene->vertex);

    LGDraw draw;
    lgIdentityMatrix(&draw.transform);
    DrawCmd* cmds;
    unsigned count;
    DrawCmd* cmd;
    unsigned offset = 0;
    lgBeginList(scene->cmdList);
    while (lgNextList(scene->cmdList, &cmds, &count)) {
        for (unsigned i = 0; i < count; i++) {
            cmd = &cmds[i];
            draw.count = cmd->count;
            draw.offset = offset;
            draw.material = cmd->material;
            draw.vertex = scene->vertex;
            lgDraw(scene->canvas, &draw);
            offset += cmd->count;
        }
    }
}

LG2D_API LGCanvas*
lg2dGetSceneCanvas(LG2DScene* scene) {
	return scene->canvas;
}

LG2D_API LG2DObj*
lg2dGetSceneRoot(LG2DScene* scene) {
	return scene->root;
}

LG2D_API void
lg2dResizeScene(LG2DScene* scene, unsigned width, unsigned height) {
    LGMatrix view;
    lgIdentityMatrix(&view);
    LGVector scale = { 2.f / width, 2.f / height, 1.f, 1.f };
    lgScaleMatrix(&view, &scale);
    lgSetCanvasView(scene->canvas, &view);
    lgResizeCanvas(scene->canvas, width, height);
}

extern void
lg2dPushSceneCmd(LG2DScene* scene,
    LGMaterial* material, unsigned count) {
    DrawCmd cmd = { count, material };
    lgPushList(scene->cmdList, &cmd, 1);
}

extern void
lg2dPushScenePoints(LG2DScene* scene,
    LG2DPoint* p, unsigned count, LGMatrix* transform) {
    LG2DPoint point;
    for (unsigned i = 0; i < count; i++) {
        point = p[i];
        lgVectorMulMatrix(&point.position, transform);
        lgPushVertexData(scene->vertex, &point, 1);
    }
}

extern void
lg2dPushSceneIndexs(LG2DScene* scene, unsigned* p, unsigned count) {
    unsigned offset = lgGetVetexTotalData(scene->vertex);
    for (unsigned i = 0; i < count; i++) {
        p[i] += offset;
    }
    lgPushVertexIndexs(scene->vertex, p, count);
}
